/**
 *  Copyright (C) 2021 Huawei Device Co., Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <jni.h>
#include <string>
#include <rtmp.h>
#include <unistd.h>
#include "rtmp_log.h"


typedef enum RTMPResult_ {
    RTMP_SUCCESS = 0,
    RTMP_READ_DONE = -1,
    RTMP_ERROR_OPEN_ALLOC = -2,
    RTMP_ERROR_OPEN_CONNECT_STREAM = -3,
    RTMP_ERROR_UNKNOWN_RTMP_OPTION = -4,
    RTMP_ERROR_UNKNOWN_RTMP_AMF_TYPE = -5,
    RTMP_ERROR_DNS_NOT_REACHABLE = -6,
    RTMP_ERROR_SOCKET_CONNECT_FAIL = -7,
    RTMP_ERROR_SOCKS_NEGOTIATION_FAIL = -8,
    RTMP_ERROR_SOCKET_CREATE_FAIL = -9,
    RTMP_ERROR_NO_SSL_TLS_SUPP = -10,
    RTMP_ERROR_HANDSHAKE_CONNECT_FAIL = -11,
    RTMP_ERROR_HANDSHAKE_FAIL = -12,
    RTMP_ERROR_CONNECT_FAIL = -13,
    RTMP_ERROR_CONNECTION_LOST = -14,
    RTMP_ERROR_KEYFRAME_TS_MISMATCH = -15,
    RTMP_ERROR_READ_CORRUPT_STREAM = -16,
    RTMP_ERROR_MEM_ALLOC_FAIL = -17,
    RTMP_ERROR_STREAM_BAD_DATASIZE = -18,
    RTMP_ERROR_PACKET_TOO_SMALL = -19,
    RTMP_ERROR_SEND_PACKET_FAIL = -20,
    RTMP_ERROR_AMF_ENCODE_FAIL = -21,
    RTMP_ERROR_URL_MISSING_PROTOCOL = -22,
    RTMP_ERROR_URL_MISSING_HOSTNAME = -23,
    RTMP_ERROR_URL_INCORRECT_PORT = -24,
    RTMP_ERROR_IGNORED = -25,
    RTMP_ERROR_GENERIC = -26,
    RTMP_ERROR_SANITY_FAIL = -27,
} RTMPResult;

jint throwIllegalStateException(JNIEnv *env, char *message) {
    jclass exception = env->FindClass("java/lang/IllegalStateException");
    return env->ThrowNew(exception, message);
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeAlloc(JNIEnv *env, jobject thiz) {
    RTMP *rtmp = RTMP_Alloc();
    return (jlong) rtmp;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeOpen(JNIEnv *env, jobject thiz, jstring url_,
                                          jboolean isPublishMode, jlong rtmpPointer,
                                          jint timeoutInMs) {
    const char *url = env->GetStringUTFChars(url_, NULL);
    RTMP *rtmp = (RTMP *) rtmpPointer;
    // rtmp = RTMP_Alloc();
    if (rtmp == NULL) {
        throwIllegalStateException(env, "RTMP open called without allocating rtmp object");
        return RTMP_ERROR_IGNORED;
    }

    RTMP_Init(rtmp);
    rtmp->Link.timeout = timeoutInMs;
    int ret = RTMP_SetupURL(rtmp, (char *) url);

    if (!ret) {
        RTMP_Free(rtmp);
        return ret;
    }
    if (isPublishMode) {
        RTMP_EnableWrite(rtmp);
    }

    ret = RTMP_Connect(rtmp, NULL);
    if (!ret) {
        RTMP_Free(rtmp);
        return ret;
    }
    ret = RTMP_ConnectStream(rtmp, 0);

    if (!ret) {
        RTMP_Free(rtmp);
        return ret;
    }
    env->ReleaseStringUTFChars(url_, url);
    return TRUE;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeRead(JNIEnv *env, jobject thiz, jbyteArray data_,
                                          jint offset, jint size, jlong rtmpPointer) {
    RTMP *rtmp = (RTMP *) rtmpPointer;
    if (rtmp == NULL) {
        throwIllegalStateException(env, "RTMP open function has to be called before read");
        return RTMP_ERROR_IGNORED;
    }

    int connected = RTMP_IsConnected(rtmp);
    if (!connected) {
        return RTMP_ERROR_CONNECTION_LOST;
    }

    char *data = (char *) malloc(size);

    int readCount = RTMP_Read(rtmp, data, size);

    if (readCount > 0) {
        env->SetByteArrayRegion(data_, offset, readCount, (const jbyte *)data);  // copy
    }
    free(data);
    return readCount;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeWrite(JNIEnv *env, jobject thiz, jbyteArray data,
                                           jint offset, jint size, jlong rtmpPointer) {
    RTMP *rtmp = (RTMP *) rtmpPointer;
    if (rtmp == NULL) {
        throwIllegalStateException(env, "RTMP open function has to be called before write");
        return RTMP_ERROR_IGNORED;
    }

    int connected = RTMP_IsConnected(rtmp);
    if (!connected) {
        return RTMP_ERROR_CONNECTION_LOST;
    }

    jbyte *buf = (jbyte *) malloc(size);
    env->GetByteArrayRegion(data, offset, size, buf);
    int result = RTMP_Write(rtmp, (const char*)buf, size);
    free(buf);
    return result;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativePause(JNIEnv *env, jobject thiz, jboolean pause,
                                           jlong rtmpPointer) {
    RTMP *rtmp = (RTMP *) rtmpPointer;
    if (rtmp == NULL) {
        throwIllegalStateException(env, "RTMP open function has to be called before pause");
        return RTMP_ERROR_IGNORED;
    }

    return RTMP_Pause(rtmp, pause);
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeIsConnected(JNIEnv *env, jobject thiz, jlong rtmpPointer) {
    RTMP *rtmp = (RTMP *) rtmpPointer;
    if (rtmp == NULL) {
        return false;
    }
    int connected = RTMP_IsConnected(rtmp);
    return connected ? true : false;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_rtmp_librtmp_RtmpUtil_nativeClose(JNIEnv *env, jobject thiz, jlong rtmpPointer) {
RTMP *rtmp = (RTMP *) rtmpPointer;
if (rtmp != NULL) {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
}
}


extern "C"
JNIEXPORT jstring JNICALL
Java_com_rtmp_librtmp_RtmpUtil_getRtmpVersion(JNIEnv *env, jclass clazz) {
char version[100];
sprintf(version, "rtmp version : %d", RTMP_LibVersion());
return env->NewStringUTF(version);
}

